package com.tianji.learning.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollStreamUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tianji.api.client.course.CatalogueClient;
import com.tianji.api.client.course.CourseClient;
import com.tianji.api.dto.course.CataSimpleInfoDTO;
import com.tianji.api.dto.course.CourseFullInfoDTO;
import com.tianji.api.dto.course.CourseSimpleInfoDTO;
import com.tianji.common.domain.dto.PageDTO;
import com.tianji.common.domain.query.PageQuery;
import com.tianji.common.exceptions.BadRequestException;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.AssertUtils;
import com.tianji.common.utils.DateUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.domain.dto.LearningPlanDTO;
import com.tianji.learning.domain.po.LearningLesson;
import com.tianji.learning.domain.po.LearningRecord;
import com.tianji.learning.domain.vo.LearningLessonVO;
import com.tianji.learning.domain.vo.LearningPlanPageVO;
import com.tianji.learning.domain.vo.LearningPlanVO;
import com.tianji.learning.enums.LessonStatus;
import com.tianji.learning.enums.PlanStatus;
import com.tianji.learning.mapper.LearningLessonMapper;
import com.tianji.learning.mapper.LearningRecordMapper;
import com.tianji.learning.service.ILearningLessonService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.LongStream;
import java.util.stream.Stream;

/**
 * <p>
 * 学生课程表 服务实现类
 * </p>
 *
 * @author waker
 * @since 2025-10-18
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class LearningLessonServiceImpl extends ServiceImpl<LearningLessonMapper, LearningLesson> implements ILearningLessonService {

    private final CourseClient courseClient;
    private final CatalogueClient catalogueClient;
    private final LearningRecordMapper learningRecordMapper;
//    private final ILearningRecordService learningRecordService;


    /**
     * 添加课程到用户课表
     *
     * @param userId
     * @param courseIds
     */
    @Override
    @Transactional
    public void addLessons(Long userId, List<Long> courseIds) {
        // 1. 远程调用课程服务，根据课程id集合查询课程信息（课程有效期/月）
        List<CourseSimpleInfoDTO> courseInfoList = courseClient.getSimpleInfoList(courseIds);
        if (ObjectUtil.isEmpty(courseInfoList) || courseInfoList.size() != courseIds.size()) {
            log.error("课程信息不存在，无法将课程加入到课表中");
            return;
        }
        // 2. 遍历课程集合，组装课表数据
        List<LearningLesson> lessons = new ArrayList<>(courseInfoList.size());
        for (CourseSimpleInfoDTO courseInfo : courseInfoList) {
            LearningLesson learningLesson = new LearningLesson();
            // 2.1 填充userId、courseId
            learningLesson.setUserId(userId);
            learningLesson.setCourseId(courseInfo.getId());
            // 2.2 填充课程过期时间(有效期+createTime)
            Integer validDuration = courseInfo.getValidDuration();
            if (ObjectUtil.isEmpty(validDuration) || validDuration <= 0) {
                continue;
            }
            learningLesson.setExpireTime(LocalDateTime.now().plusMonths(validDuration));
            lessons.add(learningLesson);
        }
        // 3. 批量插入
        this.saveBatch(lessons);
        log.info("用户：{}的课程：{} 加入到课表成功", userId, courseIds);
    }

    /**
     * 分页查询我的课表
     * select * from learning_lesson where user_id = ? order by latest_learn_time desc limit ?,?
     *
     * @param pageQuery
     * @return
     */
    @Override
    public PageDTO<LearningLessonVO> PageQueryMyLessons(PageQuery pageQuery) {
        // 1. 获取当前登陆人（从ThreadLocal获取UserId）
        Long userId = UserContext.getUser();
        // 2. 发起分页查询
        Page<LearningLesson> page = Page.of(pageQuery.getPageNo(), pageQuery.getPageSize());
/*        if (ObjectUtil.isEmpty(pageQuery.getSortBy())){
            page.addOrder(OrderItem.desc("latest_learn_time"));
        }else {
            page.addOrder(new OrderItem(pageQuery.getSortBy(),pageQuery.getIsAsc()));
        }*/
        Page<LearningLesson> pageResult = this.lambdaQuery().eq(LearningLesson::getUserId, userId)
                .page(pageQuery.toMpPage("latest_learn_time", false));
        List<LearningLesson> records = pageResult.getRecords();
        if (ObjectUtil.isEmpty(records)) {
            return PageDTO.empty(pageResult);
        }

        // 3. 发起远程调用，获取课程信息（课程名称、课程封面、总课时数）
        Map<Long, CourseSimpleInfoDTO> courseInfoMap = queryCourseInfoAnd2Map(records);

        // 4. 循环分页结果，将po list 封装未 vo list
        List<LearningLessonVO> voList = new ArrayList<>(records.size());
        for (LearningLesson record : records) {
            // 4.1 拷贝 po 至 vo
            LearningLessonVO lessonVO = BeanUtil.toBean(record, LearningLessonVO.class);
            // 4.2 获取课程信息(课程名称，封面，总课时数)
            CourseSimpleInfoDTO course = courseInfoMap.get(record.getCourseId());
            lessonVO.setCourseName(course.getName());
            lessonVO.setCourseCoverUrl(course.getCoverUrl());
            lessonVO.setSections(course.getSectionNum());
            // 4.3 存入vo集合
            voList.add(lessonVO);
        }
        // 5. 封装最终的PageDTO并返回
        return PageDTO.of(pageResult, voList);
    }

    /**
     * 查询当前用户正在学习的课程
     * select * from learning_lesson where user_id = ? and status = 1 order by latest_learn_time desc limit 1
     *
     * @return
     */
    @Override
    public LearningLessonVO queryMyCurrentLesson() {
        // 1. 获取当前登录用户
        Long userId = UserContext.getUser();
        // 2. 查询当前用户正在学习的课程
        LearningLesson lesson = lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getStatus, LessonStatus.LEARNING.getValue())
                .orderByDesc(LearningLesson::getLatestLearnTime)
                .last("limit 1")
                .one();
        if (ObjectUtil.isEmpty(lesson)) {
            return null;
        }
        // 3. 拷贝po至vo
        LearningLessonVO lessonVO = BeanUtil.toBean(lesson, LearningLessonVO.class);
        // 4. 获取课程信息
        CourseFullInfoDTO courseInfo = courseClient.getCourseInfoById(lesson.getCourseId(), false, false);
        if (ObjectUtil.isEmpty(courseInfo)) {
            throw new BadRequestException("课程信息不存在！");
        }
        // 5. 统计课程总数
        Integer courseAmount = lambdaQuery().eq(LearningLesson::getUserId, userId)
                .count();
        // 6. 获取小结信息
        List<CataSimpleInfoDTO> catalogueInfo = catalogueClient.batchQueryCatalogue(List.of(lesson.getLatestSectionId()));
        // 7. 补全信息（课程信息[课程名称，课程总结数，课程封面]，课程总数，最近一次学习的小节名称和序号）
        // 7.1 课程信息
        lessonVO.setCourseName(courseInfo.getName());
        lessonVO.setSections(courseInfo.getSectionNum());
        lessonVO.setCourseCoverUrl(courseInfo.getCoverUrl());
        // 7.2 课程总数
        lessonVO.setCourseAmount(courseAmount);
        // 7.3 最近一次学习的小节名称和序号
        if (ObjectUtil.isNotEmpty(catalogueInfo)) {
            lessonVO.setLatestSectionName(catalogueInfo.get(0).getName());
            lessonVO.setLatestSectionIndex(catalogueInfo.get(0).getCIndex());
        }
        // 8. 返回
        return lessonVO;
    }

    /**
     * 用户删除课程（已失效的课程）
     *
     * @param courseId
     */
    @Override
    public void deleteMyLesson(Long courseId) {
        lambdaUpdate().eq(LearningLesson::getCourseId, courseId)
                .eq(LearningLesson::getUserId, UserContext.getUser())
                .eq(LearningLesson::getStatus, LessonStatus.EXPIRED.getValue())
                .remove();
    }

    /**
     * 删除用户课程（退款的课程）
     *
     * @param userId
     * @param courseIds
     */
    @Override
    public void deleteRefundLesson(Long userId, List<Long> courseIds) {
        lambdaUpdate()
                .eq(LearningLesson::getUserId, userId)
                .in(LearningLesson::getCourseId, courseIds)
                .remove();
        log.info("用户：{}的课程：{} 从课表删除成功", userId, courseIds);
    }

    /**
     * 检查课程是否有效
     *
     * @param courseId
     * @return
     */
    @Override
    public Long isLessonValid(Long courseId) {
        // 1. 获取当前登录用户
        Long userId = UserContext.getUser();
        // 2. 检查课程是否在当前用户课程表内
        LearningLesson lesson = lambdaQuery().eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getCourseId, courseId)
                .one();
        // 3. 课程不存在返回null
        if (ObjectUtil.isEmpty(lesson)) {
            return null;
        }
        // 4. 课程已失效返回null
        if (lesson.getStatus().equals(LessonStatus.EXPIRED.getValue())) {
            return null;
        }
        // 5. 课程有效返回课表id
        return lesson.getId();
    }

    /**
     * 根据id查询用户课表中指定课程的状态
     *
     * @param courseId
     * @return
     */
    @Override
    public LearningLessonVO getCourseStatusById(Long courseId) {
        // 1. 获取当前登录用户
        Long userId = UserContext.getUser();
        // 2. 查询用户课表中指定课程的信息
        LearningLesson lesson = lambdaQuery().eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getCourseId, courseId)
                .one();
        if (ObjectUtil.isEmpty(lesson)) {
            return null;
        }
        // 3. 将po转为vo 并返回
        return BeanUtil.toBean(lesson, LearningLessonVO.class);
    }

    /**
     * 统计课程的学习人数
     *
     * @param courseId
     * @return
     */
    @Override
    public Integer countLearningLessonByCourse(Long courseId) {
        return lambdaQuery().eq(LearningLesson::getCourseId, courseId)
                .in(LearningLesson::getStatus,
                        LessonStatus.LEARNING.getValue(),
                        LessonStatus.FINISHED.getValue(),
                        LessonStatus.NOT_BEGIN.getValue()
                )
                .count();
    }

    /**
     * 创建或修改学习计划
     *
     * @param learningPlanDTO
     */
    @Override
    public void createPlans(LearningPlanDTO learningPlanDTO) {

        LearningLesson lesson = lambdaQuery().eq(LearningLesson::getCourseId, learningPlanDTO.getCourseId())
                .eq(LearningLesson::getUserId, UserContext.getUser())
                .one();
        AssertUtils.isNotNull(lesson, "课程不存在！无法创建学习计划");

        lambdaUpdate()
                .set(LearningLesson::getWeekFreq, learningPlanDTO.getFreq())
                .set(lesson.getPlanStatus() == PlanStatus.NO_PLAN, LearningLesson::getPlanStatus, PlanStatus.PLAN_RUNNING)
                .eq(LearningLesson::getId, lesson.getId())
                .update();
    }

    /**
     * 分页查询我的学习计划
     * @param pageQuery
     * @return
     */
    @Override
    public LearningPlanPageVO queryMyPlans(PageQuery pageQuery) {
        LearningPlanPageVO vo = new LearningPlanPageVO();
        // 1. 分页查询我的课表（本周计划学习小节数、已学完小节数、最近一次学习时间、课程id） -我的、制定过学习计划的、状态是运行中的、课表状态是 未学习和学习中的
        // select * from learning_lesson where user_id = #{userId} and plan_status = 'PLAN_RUNNING' and status in ('LEARNING', 'NOT_BEGIN')
        // order by latest_learn_time desc limit #{pageQuery.pageSize} offset #{pageQuery.pageNum}
        Page<LearningLesson> pageResult = lambdaQuery()
                .eq(LearningLesson::getUserId, UserContext.getUser())
                .eq(LearningLesson::getPlanStatus, PlanStatus.PLAN_RUNNING)
                .in(LearningLesson::getStatus, LessonStatus.LEARNING, LessonStatus.NOT_BEGIN)
                .page(pageQuery.toMpPage("latest_learn_time", false));
        List<LearningLesson> records = pageResult.getRecords();
        if (ObjectUtil.isEmpty(records)) {
            return vo.emptyPage(pageResult);
        }
        // 2. 收集课程ID集合，远程调用查询课程信息并转为Map(Map -> 课程ID:课程对象)
        Map<Long, CourseSimpleInfoDTO> courseInfoMap = queryCourseInfoAnd2Map(records);

        // 3. 统计本周实际学习小节数（Map -> 课表ID:实际学习小节数）
        // select lesson_id,COUNT(1) from learning_record where user_id = #{userId} and finished = 1
        // and finish_time between #{weekStartTime} and #{weekEndTime} group by lesson_id
        // 3.1 获取本周开始和结束的时间
        LocalDateTime begin = DateUtils.getWeekBeginTime(LocalDate.now());
        LocalDateTime end = DateUtils.getWeekEndTime(LocalDate.now());

        // 3.2 发起分组查询
        QueryWrapper<LearningRecord> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("lesson_id,COUNT(1) num")
                .eq("user_id", UserContext.getUser())
                .eq("finished", true)
                .between("finish_time", begin, end)
                .groupBy("lesson_id");
        List<Map<String, Object>> mapList = this.learningRecordMapper.selectMaps(queryWrapper);
        // mapList
            // Map1
                // "lesson_id"
                // "num"
            // Map1
                // "lesson_id"
                // "num"

        // map
            // lessonId : num
            // lessonId : num

        Map<Long, Long> learnedSessionNumMap = mapList.stream().collect(Collectors.toMap(
                map -> (Long) map.get("lesson_id"),
                map -> (Long) map.get("num"))
        );

/*        List<LearningRecord> objectList = learningRecordMapper.selectList(queryWrapper);
        Map<Long, Integer> learnedSessionNumMap2 = objectList.stream().collect(Collectors.toMap(LearningRecord::getLessonId, LearningRecord::getNum));*/


        // 4. 循环分页课表数据，补全缺失数据
        List<LearningPlanVO> voList = new ArrayList<>(records.size());
        for (LearningLesson record : records) {
            // 4.1 拷贝数据至vo
            LearningPlanVO learningPlanVO = BeanUtil.toBean(record, LearningPlanVO.class);
            // 4.2 补全课程信息
            CourseSimpleInfoDTO courseInfo = courseInfoMap.get(record.getCourseId());
            if (ObjectUtil.isNotEmpty(courseInfo)){
                learningPlanVO.setCourseName(courseInfo.getName());
                learningPlanVO.setSections(courseInfo.getSectionNum());
            }
            // 4.3 补全本周已学习小节数
            learningPlanVO.setWeekLearnedSections(learnedSessionNumMap.getOrDefault(record.getId(), 0L).intValue());

            // 4.4 添加至集合
            voList.add(learningPlanVO);
        }

        // 5. 计算本周已学习小节总数
        Collection<Long> values = learnedSessionNumMap.values();
        long count = values.stream().mapToLong(Long::longValue).sum();
        long count1 = values.stream().flatMapToLong(p -> LongStream.of(p)).sum();
        long count2 = values.stream().map(Function.identity()).reduce(Long::sum).get();
        long count3 = values.stream().collect(Collectors.summingLong(num -> num));
        long count4 = values.stream().mapToLong(Long::intValue).summaryStatistics().getSum();

        vo.setWeekFinished(Long.valueOf(count).intValue());

        // 6. 获取本周计划学习小节数
        // select sum(week_freq) as total_weekfreq from learning_lesson where user_id = #{userId} and plan_status = 'PLAN_RUNNING' and status in ('LEARNING', 'NOT_BEGIN')
        LearningLesson lesson = this.query()
                .select("sum(week_freq) as total_weekfreq")
                .eq("user_id", UserContext.getUser())
                .eq("plan_status", PlanStatus.PLAN_RUNNING)
                .in("status", LessonStatus.LEARNING, LessonStatus.NOT_BEGIN)
                .one();

        vo.setWeekTotalPlan(lesson.getTotalWeekfreq());

        // 7. 封装数据并返回结果
        return vo.pageInfo(pageResult.getTotal(),pageResult.getPages(), voList);
    }

    /**
     * 收集课程id，远程调用查询课程信息并转为Map
     *
     * @param records
     * @return
     */
    private Map<Long, CourseSimpleInfoDTO> queryCourseInfoAnd2Map(List<LearningLesson> records) {
        // 1 收集当前页面的所有课程id集合
        Set<Long> courseIds = records.stream().map(LearningLesson::getCourseId).collect(Collectors.toSet());
//        Set<Long> courseIds = CollStreamUtil.toSet(records, LearningLesson::getCourseId);

        // 2 远程调用课程服务
        List<CourseSimpleInfoDTO> courseInfoList = courseClient.getSimpleInfoList(courseIds);
        // 3 健壮性判断
        if (ObjectUtil.isEmpty(courseInfoList)) {
            throw new BizIllegalException("课程信息不存在，无法查询我的课表！");
        }
        // 4 将List转为Map，将课程信息缓存起来，供后续高效使用
//        Map<Long, CourseSimpleInfoDTO> courseInfoMap = courseInfoList.stream().collect(Collectors.toMap(CourseSimpleInfoDTO::getId, Function.identity()));
//        Map<Long, CourseSimpleInfoDTO> courseInfoMap1 = CollStreamUtil.toMap(courseInfoList, CourseSimpleInfoDTO::getId, Function.identity());
//        Map<Long, CourseSimpleInfoDTO> courseInfoMap2 = CollStreamUtil.toIdentityMap(courseInfoList, CourseSimpleInfoDTO::getId);
        return CollStreamUtil.toIdentityMap(courseInfoList, CourseSimpleInfoDTO::getId);
    }
}
